package cn.iocoder.yudao.module.trade.order.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.api.dine.enums.DineStatusEnum;
import cn.iocoder.yudao.module.api.shop.staff.ShopStaffApi;
import cn.iocoder.yudao.module.api.shop.staff.enums.ShopStaffRoleEnum;
import cn.iocoder.yudao.module.api.shop.staff.enums.ShopStaffStatusEnum;
import cn.iocoder.yudao.module.api.shop.staff.vo.ShopStaffVO;
import cn.iocoder.yudao.module.trade.order.dal.dataobject.TradeOrderDO;
import cn.iocoder.yudao.module.trade.order.dal.mysql.TradeOrderMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <pre>
 * OOoO0OOoO0OOOooo0oOOOO0OOOOO0oooOO0ooOOO0Ooooo0OOOOo0ooooO0OOooo0Ooooo0OOOOO
 *  这里是文件说明
 * OOoO0OOoO0OOOooo0oOOOO0OOOOO0oooOO0ooOOO0Ooooo0OOOOo0ooooO0OOooo0Ooooo0OOOOO
 * </pre>
 *
 * @author 山野羡民（1032694760@qq.com）
 * @since 2025/08/20
 */
@Slf4j
@Service
public class TradeOrderAssignmentService {
    @Resource
    private TradeOrderMapper tradeOrderMapper;

    @Resource
    private ShopStaffApi shopStaffApi;

    @Transactional(rollbackFor = Exception.class)
    public int autoAssignOrder() {
        // 查询未分派配送人员的订单 TODO 派单超时的情况后续再考虑如何处理
        List<TradeOrderDO> assignableOrders = tradeOrderMapper.selectAssignableList();
        if (CollUtil.isEmpty(assignableOrders)) {
            log.debug("[autoAssignOrder][查询到没有分派配送人员的订单数量为 0]");
            return 0;
        }
        Map<Long, List<TradeOrderDO>> shopAssignableOrders = new HashMap<>();
        for (TradeOrderDO assignableOrder : assignableOrders) {
            // 使用店铺编号作为分组键
            shopAssignableOrders.computeIfAbsent(assignableOrder.getShopId(), k -> new ArrayList<>()).add(assignableOrder);
        }
        int count = 0;
        for (Map.Entry<Long, List<TradeOrderDO>> entry : shopAssignableOrders.entrySet()) {
            count += autoAssignOrder(entry.getKey(), entry.getValue());
        }
        return count;
    }

    private int autoAssignOrder(Long shopId, List<TradeOrderDO> assignableOrders) {
        // 查询商家所有配送人员
        List<ShopStaffVO> staffs = shopStaffApi.getByShopIdAndRole(shopId, ShopStaffRoleEnum.RIDER.getValue());
        if (CollUtil.isEmpty(staffs)) {
            log.warn("[autoAssignOrder][查询到店铺({})配送人员数量为 0]", shopId);
            return 0;
        }
        int count = 0;
        // 5分钟内同一个配送范围的订单分派给同一个配送人员
        List<TradeOrderDO> assigned5minuteOrders = tradeOrderMapper.selectAssignedButNotArrivalOrders(shopId, 5);
        if (CollUtil.isNotEmpty(assigned5minuteOrders)) {
            List<TradeOrderDO> assignmentOrders = sameAssignmentTradeOrders(shopId, assignableOrders, assigned5minuteOrders);
            if (CollUtil.isNotEmpty(assignmentOrders)) {
                count += assignmentOrders.size();
                tradeOrderMapper.updateById(assignmentOrders);
                // 排除已分派给同一个配送人员的订单，其他订单继续分派
                List<TradeOrderDO> newAssignableOrders = new ArrayList<>();
                List<Long> assignmentOrderIds = assignmentOrders.stream().map(TradeOrderDO::getId).toList();
                for (TradeOrderDO assignableOrder : assignableOrders) {
                    if (!assignmentOrderIds.contains(assignableOrder.getId())) {
                        newAssignableOrders.add(assignableOrder);
                    }
                }
                assignableOrders.clear();
                assignableOrders.addAll(newAssignableOrders);
            }
        }
        // 获取空闲中的配送人员
        staffs = staffs.stream().filter(c -> Objects.equals(ShopStaffStatusEnum.IDLE.getValue(), c.getStatus())).toList();
        if (CollUtil.isEmpty(staffs)) {
            log.warn("[autoAssignOrder][店铺({})空闲中的配送人员数量为 0]", shopId);
            return 0;
        }
        // 优先分派给当日未送过单的配送人员
        List<ShopStaffVO> neverArrivedStaffs = staffs.stream()
                .filter(c -> c.getLastArrivedTime() == null || !LocalDateTimeUtil.isSameDay(c.getLastArrivedTime(), LocalDateTimeUtil.now()))
                .toList();
        if (CollUtil.isNotEmpty(neverArrivedStaffs)) {
            // 按一定的规则派单
            count += performAssignOrder(shopId, assignableOrders, neverArrivedStaffs);
            return count;
        }
        // 已送过单的配送人员按送达时间先后顺序排序
        List<ShopStaffVO> arrivedStaffs = staffs.stream()
                .filter(c -> c.getLastArrivedTime() != null && LocalDateTimeUtil.isSameDay(c.getLastArrivedTime(), LocalDateTimeUtil.now()))
                .sorted(Comparator.comparing(ShopStaffVO::getLastArrivedTime))
                .toList();
        // 所有配送人员都忙则等待
        if (CollUtil.isEmpty(arrivedStaffs)) {
            log.warn("[autoAssignOrder] 店铺({})没有空闲的配送人员，派单等待中", shopId);
            return count;
        }
        // 按一定的规则派单
        count += performAssignOrder(shopId, assignableOrders, arrivedStaffs);
        return count;
    }

    private List<TradeOrderDO> sameAssignmentTradeOrders(Long shopId, List<TradeOrderDO> assignableOrders, List<TradeOrderDO> assigned5minuteOrders) {
        List<TradeOrderDO> assignmentOrders = new ArrayList<>();
        for (TradeOrderDO assignableOrder : assignableOrders) {
            for (TradeOrderDO assigned5minuteOrder : assigned5minuteOrders) {
                // 外卖订单的送餐地址为商家设定的配送范围+空格+用户填写的门牌号
                String assignableOrderDetailAddress = StrUtil.subBefore(assignableOrder.getReceiverDetailAddress(), " ", false);
                String assigned5minuteOrderDetailAddress = StrUtil.subBefore(assigned5minuteOrder.getReceiverDetailAddress(), " ", false);
                if (StrUtil.equals(assignableOrderDetailAddress, assigned5minuteOrderDetailAddress)) {
                    log.info("[autoAssignOrder] 店铺({})待分派订单({})与已分派订单({})的送餐范围相同，分派给同一个配送人员({})", shopId, assignableOrder.getNo(), assigned5minuteOrder.getNo(), assigned5minuteOrder.getRiderStaffId());
                    TradeOrderDO assignmentOrder = new TradeOrderDO();
                    assignmentOrder.setId(assignableOrder.getId());
                    // 分派配送人员
                    assignmentOrder.setRiderStaffId(assigned5minuteOrder.getRiderStaffId());
                    assignmentOrder.setAssignedTime(LocalDateTime.now());
                    // 更新点餐状态
                    assignmentOrder.setDineStatus(DineStatusEnum.ASSIGNED.getValue());
                    assignmentOrders.add(assignmentOrder);
                    // 更新配送人员状态
                    shopStaffApi.updateStaffStatus(assigned5minuteOrder.getRiderStaffId(), ShopStaffStatusEnum.BUSY.getValue());
                    break;
                }
            }
        }
        return assignmentOrders;
    }

    private int performAssignOrder(Long shopId, List<TradeOrderDO> orders, List<ShopStaffVO> staffs) {
        for (TradeOrderDO order : orders) {
            order.setReceiverDetailAddress(StrUtil.subBefore(order.getReceiverDetailAddress(), " ", false));
        }
        // 按送餐地址分组订单
        Map<String, List<TradeOrderDO>> ordersByAddress = new HashMap<>();
        for (TradeOrderDO order : orders) {
            // 使用送餐地址作为分组键
            ordersByAddress.computeIfAbsent(order.getReceiverDetailAddress(), k -> new ArrayList<>()).add(order);
        }
        // 获取所有地址列表
        List<String> addresses = new ArrayList<>(ordersByAddress.keySet());
        // 分派订单给配送人员，确保同一地址的订单分派给同一配送人员
        Map<Long, List<TradeOrderDO>> ordersToAssign = new HashMap<>();
        int staffIndex = 0;
        // 如果配送人员数量少于地址数量，只分派前 staffs.size() 个地址的订单，反之则分派所有地址的订单
        int assignableAddressCount = Math.min(staffs.size(), addresses.size());
        for (int i = 0; i < assignableAddressCount; i++) {
            // 将该地址的所有订单分派给选定的配送人员
            ordersToAssign.computeIfAbsent(staffs.get(staffIndex).getId(), k -> new ArrayList<>()).addAll(ordersByAddress.get(addresses.get(i)));
            staffIndex++;
        }
        // 如果有订单需要分派（即有足够配送人员）
        if (CollUtil.isNotEmpty(ordersToAssign)) {
            performAssignOrder(ordersToAssign);
        }
        // 记录日志
        if (staffs.size() < addresses.size()) {
            log.warn("[performAssignOrder] 店铺({})配送人员数量({})少于送餐地址数量({})，部分订单等待分派", shopId, staffs.size(), addresses.size());
        }
        return ordersToAssign.size();
    }

    private void performAssignOrder(Map<Long, List<TradeOrderDO>> ordersToAssign) {
        ordersToAssign.forEach((staffId, orderList) -> {
            List<TradeOrderDO> assignmentOrders = orderList.stream()
                    .map(order -> {
                        TradeOrderDO assignmentOrder = new TradeOrderDO();
                        assignmentOrder.setId(order.getId());
                        // 分配配送人员
                        assignmentOrder.setRiderStaffId(staffId);
                        assignmentOrder.setAssignedTime(LocalDateTime.now());
                        // 更新点餐状态
                        assignmentOrder.setDineStatus(DineStatusEnum.ASSIGNED.getValue());
                        return assignmentOrder;
                    })
                    .collect(Collectors.toList());
            tradeOrderMapper.updateById(assignmentOrders);
            // 更新配送人员状态
            shopStaffApi.updateStaffStatus(staffId, ShopStaffStatusEnum.BUSY.getValue());
        });
    }

}
